#include "gd32w51x.h"
#include "stdio.h"
#include "stdint.h"
#include "string.h"
#include "DevicesSPI.h"


uint16_t g_SPI0SendDMABuff[SPI0_DMA_SEND_LENGTH + 4] = {0};
uint16_t g_SPI1SendDMABuff[SPI1_DMA_SEND_LENGTH + 8] = {0};


void vSPI1Init(void)
{
    spi_parameter_struct spi_init_struct = {0};

    /* enable GPIOx clock */
    rcu_periph_clock_enable(RCU_GPIOB);
    /* enable SPIx clock */
    rcu_periph_clock_enable(RCU_SPI1);

    /* SPI1 GPIO config: SCK/PB13, MISO/PB14, MOSI/PB9 */
    gpio_af_set(GPIOB, GPIO_AF_5, GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_9);
    gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_9);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_166MHZ, GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_9);

    /* NSS/PB12 */
    gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_12);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_166MHZ, GPIO_PIN_12);

    SET_SPI1_NSS_HIGH();

    /* SPIx parameter config */
    spi_init_struct.trans_mode           = SPI_TRANSMODE_FULLDUPLEX;
    spi_init_struct.device_mode          = SPI_MASTER;
    spi_init_struct.frame_size           = SPI_FRAMESIZE_8BIT;
    spi_init_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_2EDGE;
    spi_init_struct.nss                  = SPI_NSS_SOFT;
    spi_init_struct.prescale             = SPI_PSC_2;
    spi_init_struct.endian               = SPI_ENDIAN_MSB;
    spi_init(SPI1, &spi_init_struct);

    /* set crc polynomial */
    spi_crc_polynomial_set(SPI1, 7);


    vSPI1DMAInit();


    nvic_irq_enable(DMA0_Channel4_IRQn, 6, 0);
    dma_flag_clear(DMA0, DMA_CH4, DMA_FLAG_FTF);
    dma_interrupt_enable(DMA0, DMA_CH4, DMA_CHXCTL_FTFIE);


    /* SPI enable */
    spi_enable(SPI1);
}

void vSPI1DMAInit(void)
{
    dma_single_data_parameter_struct dma_init_struct = {0};

    /* enable DMA clock */
    rcu_periph_clock_enable(RCU_DMA0);

    /* deinitialize DMA channelx */
    dma_deinit(DMA0, DMA_CH4);
    dma_single_data_para_struct_init(&dma_init_struct);
    dma_init_struct.periph_addr = (uint32_t)(&SPI_DATA(SPI1));
    dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
    dma_init_struct.memory0_addr = 0;
    dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
    dma_init_struct.periph_memory_width = DMA_MEMORY_WIDTH_8BIT;
    dma_init_struct.circular_mode = DMA_CIRCULAR_MODE_DISABLE;
    dma_init_struct.direction = DMA_MEMORY_TO_PERIPH;
    dma_init_struct.number = 0;
    dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
    dma_single_data_mode_init(DMA0, DMA_CH4, &dma_init_struct);

    /* DMA channel peripheral select */
    dma_channel_subperipheral_select(DMA0, DMA_CH4, DMA_SUBPERI0);

    /* disable DMA channel */
    dma_channel_disable(DMA0, DMA_CH4);
    /* SPIx DMA enable for transmission */
    spi_dma_enable(SPI1, SPI_DMA_TRANSMIT);
}

uint8_t ucSPIWriteReadByte(uint32_t uiSpiPeriph, uint8_t ucByte)
{
    while(RESET == spi_i2s_flag_get(uiSpiPeriph, SPI_FLAG_TBE));
    spi_i2s_data_transmit(uiSpiPeriph, ucByte);

    while(RESET == spi_i2s_flag_get(uiSpiPeriph, SPI_FLAG_RBNE));
    ucByte = spi_i2s_data_receive(uiSpiPeriph);

    return ucByte;
}

int8_t cSPIWriteDatas(uint32_t uiSpiPeriph, uint8_t *pBuffer, int32_t iLength)
{
    int8_t cError = 0;

    if(pBuffer == NULL)
        return -1;

    while((iLength--) > 0)
    {
        while(RESET == spi_i2s_flag_get(uiSpiPeriph, SPI_FLAG_TBE));
        spi_i2s_data_transmit(uiSpiPeriph, *pBuffer++);
    }

    return cError;
}

int8_t cSPIReadDatas(uint32_t uiSpiPeriph, uint8_t *pBuffer, int32_t iLength)
{
    int8_t cError = 0;

    if(pBuffer == NULL)
        return -1;

    while((iLength--) > 0)
    {
        while(RESET == spi_i2s_flag_get(uiSpiPeriph, SPI_FLAG_TBE));
        spi_i2s_data_transmit(uiSpiPeriph, 0xFFFF);

        while(RESET == spi_i2s_flag_get(uiSpiPeriph, SPI_FLAG_RBNE));
        *pBuffer++ = spi_i2s_data_receive(uiSpiPeriph);
    }

    return cError;
}



/**
 * @brief  设置DMA的数据传输长度，并启动DMA传输
 * @param  DMAy_Channelx：   相应DMA的通道
           Length：          传输长度
 * @retval 无
 */
void vSPIxDMASetLenthStart(uint32_t dma_periph, dma_channel_enum channelx, uint32_t uiLength)
{
    uint32_t uiTime = 0;

    uiTime = 1000000;
    /* Disable the peripheral */
    dma_channel_disable(dma_periph, channelx);
    while(((DMA_CHCTL(dma_periph, channelx) & DMA_CHXCTL_CHEN) != 0) && (--uiTime));

    /* 必须要清除所有中断状态，才允许使能DMA */
    dma_interrupt_flag_clear(dma_periph, channelx, DMA_INT_SDE | DMA_INT_TAE | DMA_INT_HTF | DMA_INT_FTF | DMA_INT_FEE);

    /* Configure DMA Stream data length */
    dma_transfer_number_config(dma_periph, channelx, uiLength);

    uiTime = 1000000;
    /* Enable the peripheral */
    dma_channel_enable(dma_periph, channelx);
    while(((DMA_CHCTL(dma_periph, channelx) & DMA_CHXCTL_CHEN) == 0) && (--uiTime));
}

/**
 * @brief  设置DMA的数据传输长度，并启动DMA传输
 * @param  DMAy_Channelx：   相应DMA的通道
           Length：          传输长度
 * @retval 无
 */
void vSPIxDMASetAddrAndLenthStart(uint32_t dma_periph, dma_channel_enum channelx, int8_t cDirection, uint32_t uiSourceAddress, uint32_t uiTargetAddress, uint32_t uiLength)
{
    uint32_t uiTime = 0;

    uiTime = 1000000;
    /* Disable the peripheral */
    dma_channel_disable(dma_periph, channelx);
    while(((DMA_CHCTL(dma_periph, channelx) & DMA_CHXCTL_CHEN) != 0) && (--uiTime));

    /* 必须要清除所有中断状态，才允许使能DMA */
    dma_interrupt_flag_clear(dma_periph, channelx, DMA_INT_SDE | DMA_INT_TAE | DMA_INT_HTF | DMA_INT_FTF | DMA_INT_FEE);

    /* configure memory base address */
    dma_memory_address_config(dma_periph, channelx, (dma_periph == DMA1) ? 1 : 0, uiSourceAddress);

    /* configure peripheral base address */
    dma_periph_address_config(dma_periph, channelx, uiTargetAddress);

    /* Configure DMA Stream data length */
    dma_transfer_number_config(dma_periph, channelx, uiLength);

    /* configure the direction of data transfer on the channel   */
    dma_transfer_direction_config(dma_periph, channelx, cDirection);

    uiTime = 1000000;
    /* Enable the Peripheral */
    dma_channel_enable(dma_periph, channelx);
    while(((DMA_CHCTL(dma_periph, channelx) & DMA_CHXCTL_CHEN) == 0) && (--uiTime));
}

void vSPIxDMASendDatas(uint32_t uiUsartPeriph, uint8_t *pucDatas, int32_t iLength)
{
    dma_channel_enum enumChannelx = DMA_CH1;
    uint32_t dma_periph = 0;
    int32_t iDMAMaxLength = 0;
    uint8_t *pucSendDatasHandle = pucDatas;

    if(iLength <= 0)
        return;

    switch(uiUsartPeriph)
    {
        case SPI0: dma_periph = DMA1; enumChannelx = DMA_CH3; pucSendDatasHandle = (uint8_t *)g_SPI0SendDMABuff; iDMAMaxLength = sizeof(g_SPI0SendDMABuff); break;
        case SPI1: dma_periph = DMA0; enumChannelx = DMA_CH4; pucSendDatasHandle = (uint8_t *)g_SPI1SendDMABuff; iDMAMaxLength = sizeof(g_SPI1SendDMABuff); break;

        default : printf("vSPIxDMASendDatas channel error.\r\n"); return;
    }

    /* 等待上次DMA传输完成 */
    while((dma_flag_get(dma_periph, enumChannelx, DMA_FLAG_FTF) == RESET) && ((DMA_CHCTL(dma_periph, enumChannelx) & DMA_CHXCTL_CHEN) != 0));

    /* 设置传输地址与长度，并启动DMA发送 */
    vSPIxDMASetAddrAndLenthStart(dma_periph, enumChannelx, DMA_MEMORY_TO_PERIPH, (uint32_t)pucDatas, (uint32_t)(&SPI_DATA(uiUsartPeriph)), iLength);

    // /* 等待本次DMA传输完成 */
    // while((dma_flag_get(dma_periph, enumChannelx, DMA_FLAG_FTF) == RESET) && ((DMA_CHCTL(dma_periph, enumChannelx) & DMA_CHXCTL_CHEN) != 0));
}
